package nl.haemers.data.reflection 
{
	import flash.errors.IllegalOperationError;
	import flash.utils.describeType;
	import flash.utils.Dictionary;
	import haemers.data.vo.PageVO;
	import nl.haemers.data.serializers.ObjectSerializer;
	import nl.haemers.data.constants.ReflectionConstants;
	import nl.haemers.data.utils.ObjectUtils;
	/**
	 * ...
	 * @author Jonathan Haemers
	 */
	// TODO: check for parameters in the constructor.
	// TODO: check for methods in the class.
	public class Type
	{
		private var _instance			:Object;
		private var _classReference		:Class;
		
		private var _name				:String;
		private var _base				:String;
		private var _extends			:String;
		
		private var _isDynamic			:Boolean;
		private var _isFinal			:Boolean;
		private var _isStatic			:Boolean;
		
		private var _accessors			:IAccessors;
		private var _variables			:IVariables;
		private var _metadatas			:IMetadatas;
		private var _methods			:IMethods;
		
		private var _level				:int;
		
		//TODO: if it's a Vector you have a Factory layer (not always true, need to check this out)
		private var _factory			:*;
		
		private var _collectionItemType	:Type;
		
		/**
		 * Constructor
		 * @param	currentLevel
		 */
		public function Type(currentLevel:int) 
		{ 
			_level = currentLevel;
		}
		
		/** Public methods **/
		public function init(ClassReference:Class) : void
		{
			_instance = new ClassReference();
			if (_instance == null)
			{
				throw new IllegalOperationError("This isn't a legal ClassReference.");
			}
			_classReference = ClassReference;
			var xml:XML = describeType(_instance);
			//var xml2:XML = describeType(ClassReference);
			//trace("xml:"+ xml);
			//trace("xml2:" + xml2);
			//trace("------------------------------------------------------------------------------------");
			
			this._metadatas = new Metadatas(xml);
			var serialize:IMetadata = this._metadatas.getMetadata(ReflectionConstants.OBJECT_ATTRIBUTE)
			
			this._name = serialize != null ? serialize.args.getArg(ReflectionConstants.ATTRIBUTE_NAME).value : xml.@name;
			this._base = xml.@base;
			this._isDynamic = xml.@isDynamic == "true";
			this._isFinal = xml.@isFinal == "true";
			this._isStatic = xml.@isStatic == "true";
			
			if (ObjectUtils.isCollectionType(this.name))
			{
				var item:Class = ObjectUtils.getCollectionItemClass(this.name);
				this._collectionItemType = ObjectSerializer.getType(item);
			}
			//this._extends = this.getExtensions(xml);
			
			this._accessors = new Accessors(xml);//Accessors.init(xml);
			this._variables = new Variables(xml);//Variables.init(xml);
			this._methods = new Methods(xml);
			//this._os = ObjectSerializer.
			//create extensions
			//create metadata
				//metadata create args
			//create accessors
				//accessors create metadata
					//metadata create args
				//Accessors CHECK for type custom objects if true
					//accessors create Type
						//Type create above
			//create variables
				//variables create metadata
					//metadata create args
				//Variables CHECK for type custom objects if true
					//variables create Type
			
			//create Factories this means we are in a Vector
				//Factory is sort of a extra layer between type and accessors, variables
		}
		
		public function createCollectionItem() : *
		{
			//This doesn't return a new Instance each time we call this method!
			return _collectionItemType != null ? _collectionItemType.createInstance() : null;
		}
		
		public function get collectionItemType() : Type
		{
			return _collectionItemType;
		}
		
		public function getProperty(name:String) : IProperty
		{
			if (_accessors.hasAccessor(name))
				return _accessors.getAccessor(name) as IProperty;
			else if (_variables.hasVariable(name))
				return _variables.getVariable(name) as IProperty;
				
			return null;
		}
		
		public function hasProperty(name:String) : Boolean
		{
			return (_accessors.hasAccessor(name) || _variables.hasVariable(name));
		}
		
		public function getProperties() : Dictionary
		{
			var key:String;
			var _props:Dictionary = new Dictionary();
			var _vars:Dictionary = _variables.getVariables();
			var _a:Dictionary = _accessors.getAccessors();
			for (key in _vars)
			{
				_props[key] = _vars[key];
			}
			for (key in _a)
			{
				_props[key] = _a[key];
			}
			return _props;
		}
		
		public function hasMethodWithParam(name:String) : Boolean
		{
			return getMethodWithParam(name) != null;
		}
		/**
		 * Searches for a method with name as Parameter. 
		 * TODO: fix note? 
		 * several options: Return a vector?(Problem is we can't determine which method to take, 
		 * 									 we could compare Datatypes, but a name param is most of the time a String)
		 * 					Throw an Error.
		 * @note If there are several methods with the same parameters(eq: Both methods have name:String) we always pick
		 * 		 the first method.
		 * @param	name
		 * @return IMethod or null
		 */
		public function getMethodWithParam(name:String) : IMethod
		{
			var key:String;
			var firstMethod:IMethod;
			var methods:Dictionary = _methods.getMethods();
			for (key in methods)
			{
				var method:IMethod = methods[key];
				if (method.parameters.hasParameter(name) && !method.ignore)
				{
					if (!method.ignore && !firstMethod)
					{
						firstMethod = method;
					}
					return method;
				}
			}
			//we couldn't find a valid method
			//Just return the first method
			return firstMethod;
		}
		
		public function getMethod(name:String) : IMethod
		{
			return _methods.getMethod(name);
		}
		
		public function hasMethod(name:String) : Boolean
		{
			return _methods.hasMethod(name);
		}
		
		public function hasParent() : Boolean
		{
			return this._level > 0;
		}
		
		//TODO: Add missing constructor parameters!
		public function createInstance() : Object
		{
			return new _classReference();
		}
		/** Getters & Setters **/
		public function get instance() : Object
		{
			return _instance;
		}
		
		public function get name() : String
		{
			return _name;
		}
		public function get base() : String
		{
			return _base;
		}
		public function get isDynamic() : Boolean
		{
			return _isDynamic;
		}
		public function get isFinal() : Boolean
		{
			return _isFinal;
		}
		public function get isStatic() : Boolean
		{
			return _isStatic;
		}
		
		public function get metadatas() : IMetadatas
		{
			return _metadatas;
		}
		
		public function get accessors() : IAccessors
		{
			return _accessors;
		}
		
		public function get variables() : IVariables
		{
			return _variables;
		}
		/** Protected methods **/
		
		/** Private methods **/
		
		/*
		<type name="nl.haemers.test.data.vo::PageVO" base="Class" isDynamic="true" isFinal="true" isStatic="true">
		  <extendsClass type="Class"/>
		  <extendsClass type="Object"/>
		  <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
		  <factory type="nl.haemers.test.data.vo::PageVO">
			<extendsClass type="Object"/>
			<accessor name="url" access="readwrite" type="String" declaredBy="nl.haemers.test.data.vo::PageVO">
			  <metadata name="Serialize">
				<arg key="name" value="page"/>
			  </metadata>
			  <metadata name="__go_to_definition_help">
				<arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\PageVO.as"/>
				<arg key="pos" value="546"/>
			  </metadata>
			  <metadata name="__go_to_definition_help">
				<arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\PageVO.as"/>
				<arg key="pos" value="611"/>
			  </metadata>
			</accessor>
			<accessor name="name" access="readwrite" type="String" declaredBy="nl.haemers.test.data.vo::PageVO">
			  <metadata name="__go_to_definition_help">
				<arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\PageVO.as"/>
				<arg key="pos" value="368"/>
			  </metadata>
			  <metadata name="__go_to_definition_help">
				<arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\PageVO.as"/>
				<arg key="pos" value="435"/>
			  </metadata>
			</accessor>
			<metadata name="Serialize">
			  <arg key="name" value="page"/>
			</metadata>
			<metadata name="__go_to_ctor_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\PageVO.as"/>
			  <arg key="pos" value="229"/>
			</metadata>
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\PageVO.as"/>
			  <arg key="pos" value="129"/>
			</metadata>
		  </factory>
		</type>
		*/
		
		/**
		 * <metadata name="Serialize">
				<arg key="name" value="environment"/>
			  </metadata>
			  <metadata name="__go_to_ctor_definition_help">
				<arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\EnvironmentVO.as"/>
				<arg key="pos" value="657"/>
			  </metadata>
			  <metadata name="__go_to_definition_help">
				<arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\EnvironmentVO.as"/>
				<arg key="pos" value="136"/>
			  </metadata>
		 * 
		 */
		
		
		/*
		<type name="nl.haemers.test.data.vo::TestVO" base="Object" isDynamic="false" isFinal="false" isStatic="false">
		  <extendsClass type="Object"/>
		   <variable name="street" type="String">
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="357"/>
			</metadata>
		  </variable>
		  <variable name="hometown" type="String">
			<metadata name="Serialize">
			  <arg key="name" value="city"/>
			</metadata>
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="422"/>
			</metadata>
		  </variable>
		  */
		  /*
		  <accessor name="addressVO" access="readwrite" type="nl.haemers.test.data.vo::AddressVO" declaredBy="nl.haemers.test.data.vo::TestVO">
			 <metadata name="Serialize">
			  <arg key="name" value="address"/>
			</metadata>
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="1522"/>
			</metadata>
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="1601"/>
			</metadata>
		  </accessor>
		  */
		  /*
		  <accessor name="id" access="readwrite" type="int" declaredBy="nl.haemers.test.data.vo::TestVO">
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="518"/>
			</metadata>
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="579"/>
			</metadata>
		  </accessor>
		  <accessor name="title" access="readwrite" type="String" declaredBy="nl.haemers.test.data.vo::TestVO">
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="411"/>
			</metadata>
			<metadata name="Serialize">
				<arg key="type" value="title"/>
			</metadata>
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="331"/>
			</metadata>
		  </accessor>
		  <accessor name="xPos" access="readwrite" type="Number" declaredBy="nl.haemers.test.data.vo::TestVO">
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="657"/>
			</metadata>
			<metadata name="__go_to_definition_help">
			  <arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			  <arg key="pos" value="725"/>
			</metadata>
		  </accessor>
		  <metadata name="__go_to_ctor_definition_help">
			<arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			<arg key="pos" value="227"/>
		  </metadata>
		  <metadata name="__go_to_definition_help">
			<arg key="file" value="D:\work\asjlib\trunk\src\nl\haemers\test\data\vo\TestVO.as"/>
			<arg key="pos" value="93"/>
		  </metadata>
		</type>
		*/
	}

}